import { ModelError } from '@ibiz-template/core';
import {
  calcDeCodeNameById,
  calcNavParams,
  getChildNodeRSs,
  getRootNode,
  getTreeNode,
  ITreeNodeData,
  MDControlService,
  TreeCodeListNodeData,
  TreeDataSetNodeData,
  TreeStaticNodeData,
} from '@ibiz-template/runtime';
import {
  IDETree,
  IDETreeCodeListNode,
  IDETreeDataSetNode,
  IDETreeNode,
  IDETreeNodeRS,
} from '@ibiz/model-core';

export interface TreeFetchOpts {
  /**
   * 是否有过滤搜索
   *
   * @type {boolean}
   * @memberof TreeFetchOpts
   */
  hasQuery: boolean;
  /**
   * 上下文
   *
   * @type {IParams}
   * @memberof TreeFetchOpts
   */
  context: IParams;
  /**
   * 视图参数
   *
   * @type {IParams}
   * @memberof TreeFetchOpts
   */
  params: IParams;

  /**
   * 默认展开节点集合
   * @author lxm
   * @date 2023-08-08 03:23:52
   * @type {string[]}
   */
  defaultExpandedKeys?: string[];

  /**
   * 是否是叶子节点
   * @author lxm
   * @date 2023-05-29 02:26:44
   * @type {boolean}
   */
  leaf?: boolean;
}

/**
 * 树部件服务
 * @author lxm
 * @date 2023-05-15 09:53:35
 * @export
 * @class GridService
 * @extends {MDControlService<IDETree>}
 */
export class TreeService extends MDControlService<IDETree> {
  /**
   * 获取子节点数据
   *
   * @param {ITreeNodeData} [parentNodeData] 父节点数据
   * @param {boolean} [hasQuery=false] 是否搜索
   * @returns {*}
   * @memberof TreeService
   */
  async fetchChildNodes(
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): Promise<ITreeNodeData[] | undefined> {
    const { hasQuery } = opts;

    let childrenNodes: ITreeNodeData[] = [];
    if (!parentNodeData) {
      // 没有父节点的加载根节点数据
      const rootNode = getRootNode(this.model);
      childrenNodes = await this.fetchNodeDatasByType(
        rootNode,
        undefined,
        undefined,
        opts,
      );
    } else {
      // 有父节点的计算节点关系查询对应节点数据并合并
      const childNodeRSs = getChildNodeRSs(this.model, {
        parentId: parentNodeData?.nodeId,
        hasQuery,
      });

      if (childNodeRSs.length === 0) {
        return;
      }
      const results = await Promise.allSettled(
        childNodeRSs.map(async childNodeRS => {
          const childNode = getTreeNode(
            this.model,
            childNodeRS.childDETreeNodeId!,
          );
          return this.fetchNodeDatasByType(
            childNode,
            childNodeRS,
            parentNodeData,
            opts,
          );
        }),
      );

      results.forEach(result => {
        if (result.status === 'fulfilled' && result.value) {
          childrenNodes.push(...result.value);
        } else if (result.status === 'rejected') {
          ibiz.log.error(result.reason);
        }
      });
    }
    return childrenNodes;
  }

  /**
   * 通过节点类型加载节点数据
   * @author lxm
   * @date 2023-08-09 03:22:47
   * @protected
   * @param {IDETreeNode} nodeModel 节点模型
   * @param {(IDETreeNodeRS | undefined)} nodeRS 与上层节点的节点关系
   * @param {(ITreeNodeData | undefined)} parentNodeData 上层节点数据
   * @param {TreeFetchOpts} opts 额外参数
   * @return {*}  {Promise<ITreeNodeData[]>}
   */
  protected async fetchNodeDatasByType(
    nodeModel: IDETreeNode,
    nodeRS: IDETreeNodeRS | undefined,
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): Promise<ITreeNodeData[]> {
    // 有没有下级
    const leaf =
      getChildNodeRSs(this.model, {
        parentId: nodeModel.id,
        hasQuery: opts.hasQuery,
      }).length === 0;
    const tempOpts = { ...opts, leaf };

    // 根据类型查询节点数据
    let result: ITreeNodeData[] = [];
    switch (nodeModel.treeNodeType) {
      case 'STATIC':
        {
          const nodeData = await this.getStaticNodeData(
            nodeModel,
            nodeRS,
            parentNodeData,
            tempOpts,
          );
          result = [nodeData];
        }
        break;
      case 'DE':
        result = await this.getDENodeDatas(
          nodeModel as IDETreeDataSetNode,
          nodeRS,
          parentNodeData,
          tempOpts,
        );
        break;
      case 'CODELIST':
        result = await this.getCodeListNodeDatas(
          nodeModel as IDETreeCodeListNode,
          nodeRS,
          parentNodeData,
          tempOpts,
        );
        break;
      default:
        throw new ModelError(
          nodeModel,
          `${nodeModel.treeNodeType}节点类型未支持`,
        );
    }

    // *查询回来之后,如果有子节点，看默认展开相关配置有时每个节点数据走一遍展开逻辑
    const { expandFirstOnly, expanded, rootNode } = nodeModel;
    const isExpandedRoot = rootNode && !this.model.rootVisible; // root节点不显示的时候默认展开其子节点
    if (!leaf) {
      await Promise.all(
        result.map(async (childNode, index) => {
          if (
            (expanded && // 全展开合只展开首节点expanded都为true
              (!expandFirstOnly || (expandFirstOnly && index === 0))) ||
            (opts.defaultExpandedKeys?.length &&
              opts.defaultExpandedKeys.includes(childNode.id)) || // 外部回显给的默认展开节点集合有的展开
            isExpandedRoot
          ) {
            const subChildrenNodes = await this.fetchChildNodes(
              childNode,
              opts,
            );
            childNode.children = subChildrenNodes;
          }
        }),
      );
    }

    return result;
  }

  /**
   * 获取静态节点数据
   *
   * @protected
   * @param {TreeStaticNodeModel} nodeModel
   * @param {boolean} [hasQuery=false]
   * @returns {*}
   * @memberof TreeService
   */
  protected async getStaticNodeData(
    nodeModel: IDETreeNode,
    nodeRS: IDETreeNodeRS | undefined,
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): Promise<ITreeNodeData> {
    const nodeData: ITreeNodeData = new TreeStaticNodeData(
      nodeModel,
      parentNodeData,
      { parentValueLevel: nodeRS?.parentValueLevel, leaf: !!opts.leaf },
    );

    return nodeData;
  }

  /**
   * 获取节点关系过滤的上下文和视图参数
   *
   * @param {TreeNodeRSModel} nodeRS 节点关系模型
   * @param {(ITreeNodeData | undefined)} parentNodeData 父节点数据
   * @returns {*}  {(IParams | undefined)}
   * @memberof TreeService
   */
  getNodeRSFilterParams(
    nodeRS: IDETreeNodeRS,
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): {
    context: IParams;
    params: IParams;
    navContext: IParams;
    navParams: IParams;
  } {
    // 处理父值过滤项
    const {
      parentValueLevel,
      navigateContexts: navContexts,
      navigateParams: navParams,
      parentFilter: navFilter,
      parentDER1N,
    } = nodeRS;

    const pickupDEFName = parentDER1N?.pickupDEFName;

    const params = { ...opts.params };

    // 合并部件上下文和父节点资源上下文
    const context = {
      ...opts.context,
      ...(parentNodeData?.context || {}),
    };

    // 计算关系过滤用到的实体数据
    let data: IData = {};
    let parentData: ITreeNodeData | undefined;
    // 根据父值级别查找父数据
    if (parentNodeData) {
      parentData = parentNodeData;
      for (let index = 1; index < parentValueLevel!; index++) {
        parentData = parentData?.parent;
      }
    }
    let derValue;
    let deName;
    if (parentData) {
      data = parentData.deData || {};
      const parentNodeModel = getTreeNode(this.model, parentData.nodeId);
      if (parentNodeModel.appDataEntityId) {
        deName = calcDeCodeNameById(parentNodeModel.appDataEntityId);
      }
      derValue = parentData.value;
    }

    // 导航相关参数处理
    const { resultContext, resultParams } = calcNavParams(
      {
        deName,
        navFilter: navFilter ? `n_${navFilter}_eq` : undefined,
        pickupDEFName,
        navParams,
        navContexts,
      },
      { derValue, context, params, data },
    );
    Object.assign(context, resultContext);
    Object.assign(params, resultParams);

    return {
      context,
      params,
      navContext: resultContext,
      navParams: resultParams,
    };
  }

  /**
   * 获取实体数据集数据
   *
   * @protected
   * @param {TreeNodeRSModel} nodeRS
   * @param {TreeFetchOpts} opts
   * @returns {*}
   * @memberof TreeService
   */
  protected async getDENodeDatas(
    nodeModel: IDETreeDataSetNode,
    nodeRS: IDETreeNodeRS | undefined,
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): Promise<TreeDataSetNodeData[]> {
    const { appDEDataSetId, appDataEntityId } = nodeModel;

    // 处理查询参数,没有关系的时候不处理参数了
    const { context, params, navContext, navParams } = nodeRS
      ? this.getNodeRSFilterParams(nodeRS, parentNodeData, opts)
      : {
          context: opts.context,
          params: opts.params,
          navContext: {},
          navParams: {},
        };

    // 设置查询长度
    params.size = nodeModel.maxSize || 1000;
    // 排序属性
    if (nodeModel.sortAppDEFieldId && nodeModel.sortDir) {
      Object.assign(params, {
        sort: `${nodeModel.sortAppDEFieldId.toLowerCase()},${nodeModel.sortDir.toLowerCase()}`,
      });
    }

    // 请求实体数据
    const response = await this.app.deService.exec(
      appDataEntityId!,
      appDEDataSetId!,
      context,
      params,
    );
    if (response.data.length) {
      const nodeDatas = response.data.map((item: IData) => {
        return new TreeDataSetNodeData(nodeModel, parentNodeData, {
          data: item,
          leaf: !!opts.leaf,
          navContext,
          navParams,
        });
      });
      return nodeDatas;
    }
    return [];
  }

  /**
   * 获取代码表节点数据
   *
   * @protected
   * @param {TreeCodeListNodeModel} _nodeModel
   * @param {TreeFetchOpts} _opts
   * @memberof TreeService
   */
  protected async getCodeListNodeDatas(
    nodeModel: IDETreeCodeListNode,
    nodeRS: IDETreeNodeRS | undefined,
    parentNodeData: ITreeNodeData | undefined,
    opts: TreeFetchOpts,
  ): Promise<TreeCodeListNodeData[]> {
    // 处理查询参数,没有关系的时候不处理参数了
    const { context, params, navContext, navParams } = nodeRS
      ? this.getNodeRSFilterParams(nodeRS, parentNodeData, opts)
      : {
          context: opts.context,
          params: opts.params,
          navContext: {},
          navParams: {},
        };

    // 请求实体数据
    const items = await this.app.codeList.get(
      nodeModel.codeListId!,
      context,
      params,
    );
    if (items.length) {
      const nodeDatas = items.map(item => {
        return new TreeCodeListNodeData(nodeModel, parentNodeData, {
          data: item,
          leaf: !!opts.leaf,
          navContext,
          navParams,
        });
      });
      return nodeDatas;
    }
    return [];
  }
}
